package runtime

import (
	"fmt"
	"gitee.com/xcy871622040/gameframe/cluster/rpcx"
	"gitee.com/xcy871622040/gameframe/cluster/settings"
	"gitee.com/xcy871622040/gameframe/cluster/settings/urlparse"
	"gitee.com/xcy871622040/gameframe/shareing"
	"github.com/rcrowley/go-metrics"
	"github.com/rpcxio/libkv/store"
	rpcxetcddis "github.com/rpcxio/rpcx-etcd/client"
	rpcxetcdreg "github.com/rpcxio/rpcx-etcd/serverplugin"
	rpcxredisdis "github.com/rpcxio/rpcx-redis/client"
	rpcxredisreg "github.com/rpcxio/rpcx-redis/serverplugin"
	"github.com/smallnest/rpcx/client"
	"log"
	"time"
)

// Runtime 运行时的服务组件 提供全局服务管理
type Runtime interface {
	Init(string, *settings.Setting, uint16) // 初始化

	Ready()    // 设置节点可以对外提供服务
	Allocate() // 设置节点被分配（处理服务中）
	Shutdown() // 设置节点准备关闭（空闲中的节点会直接关闭）

	ServiceName() string     // 节点服务名字
	ExternalAddress() string // 节点对外地址

	GenerateServerId() string // 获取当前服务的id

	Register() *rpcx.RegisterPlugin                     // 注册中心组件
	Discovery(servicepath string) *rpcx.DiscoveryPlugin // 服务发现组件
}

type ShareingPlugin struct {
	registerPlugin  shareing.Once[*rpcx.RegisterPlugin]
	discoveryPlugin func(servicepath string) *rpcx.DiscoveryPlugin
}

// Register 注册中心组件
func (sp *ShareingPlugin) Register() *rpcx.RegisterPlugin {
	return sp.registerPlugin.Do()
}

// Discovery 服务发现组件
func (sp *ShareingPlugin) Discovery(servicepath string) *rpcx.DiscoveryPlugin {
	return sp.discoveryPlugin(servicepath)
}

// EtcdRegisterPlugin 创建etcd服务注册组件
func (sp *ShareingPlugin) EtcdRegisterPlugin(serviceaddress, etcdurl, basepath string) {
	parse := urlparse.Parse{}
	if err := parse.SetParseUrl(etcdurl); err != nil {
		log.Fatal("etcd url parse ERROR:", err)
	}
	options := &store.Config{
		Bucket:   parse.Kwargs["db"],
		Password: parse.PassWord,
		Username: parse.UserName,
	}

	sp.registerPlugin = shareing.Once[*rpcx.RegisterPlugin]{
		New: func() *rpcx.RegisterPlugin {
			registerplugin := &rpcxetcdreg.EtcdV3RegisterPlugin{
				ServiceAddress: serviceaddress,
				EtcdServers:    []string{parse.Address},
				BasePath:       basepath,
				Metrics:        metrics.NewRegistry(),
				UpdateInterval: 5 * time.Second,
				Options:        options,
			}

			fmt.Printf("%#v", registerplugin)
			if err := registerplugin.Start(); err != nil {
				log.Fatal("EtcdRegisterPlugin init ERROR:", err)
			}

			return &rpcx.RegisterPlugin{
				RegisterPlugin: registerplugin,
				AttrPlugin: rpcx.AttrPlugin{
					BASEPATH: basepath,
				},
			}
		},
	}
}

// EtcdDiscovery 创建Etcd服务发现组件
func (sp *ShareingPlugin) EtcdDiscovery(etcdurl, basepath string) {
	parse := urlparse.Parse{}
	if err := parse.SetParseUrl(etcdurl); err != nil {
		log.Fatal("etcd url parse ERROR:", err)
	}
	options := &store.Config{
		Bucket:   parse.Kwargs["db"],
		Password: parse.PassWord,
		Username: parse.UserName,
	}

	var DiscoveryPluginStory = map[string]*rpcx.DiscoveryPlugin{}

	sp.discoveryPlugin = func(servicepath string) *rpcx.DiscoveryPlugin {
		if discovery, ok := DiscoveryPluginStory[servicepath]; ok {
			return discovery
		} else {
			if dis, err := rpcxetcddis.NewEtcdV3Discovery(basepath, servicepath, []string{parse.Address}, false, options); err != nil {
				log.Fatal("rpcx NewEtcdDiscovery ERROR:", err)
			} else {
				DiscoveryPluginStory[servicepath] = &rpcx.DiscoveryPlugin{
					ServiceDiscovery: dis,
					AttrPlugin: rpcx.AttrPlugin{
						BASEPATH: basepath,
					},
				}
			}
		}
		return DiscoveryPluginStory[servicepath]
	}
}

// RedisRegisterPlugin 创建redis服务注册组件
func (sp *ShareingPlugin) RedisRegisterPlugin(serviceaddress, redisurl, basepath string) {
	parse := urlparse.Parse{}
	if err := parse.SetParseUrl(redisurl); err != nil {
		log.Fatal("redis url parse ERROR:", err)
	}

	options := &store.Config{
		Bucket:   parse.Kwargs["db"],
		Password: parse.PassWord,
	}

	sp.registerPlugin = shareing.Once[*rpcx.RegisterPlugin]{
		New: func() *rpcx.RegisterPlugin {
			registerplugin := &rpcxredisreg.RedisRegisterPlugin{
				ServiceAddress: serviceaddress,
				BasePath:       basepath,
				RedisServers:   []string{parse.Address},
				UpdateInterval: 5 * time.Second,
				Options:        options,
			}

			if err := registerplugin.Start(); err != nil {
				log.Fatal("RedisRegisterPlugin init ERROR:", err)
			}

			return &rpcx.RegisterPlugin{
				RegisterPlugin: registerplugin,
				AttrPlugin: rpcx.AttrPlugin{
					BASEPATH: basepath,
				},
			}
		},
	}
}

// RedisDiscovery 创建Redis服务发现组件
func (sp *ShareingPlugin) RedisDiscovery(redisurl, basepath string) {
	parse := urlparse.Parse{}
	if err := parse.SetParseUrl(redisurl); err != nil {
		log.Fatal("redis url parse ERROR:", err)
	}

	options := &store.Config{
		Bucket:   parse.Kwargs["db"],
		Password: parse.PassWord,
	}

	var DiscoveryPluginStory = map[string]*rpcx.DiscoveryPlugin{}
	sp.discoveryPlugin = func(servicepath string) *rpcx.DiscoveryPlugin {
		if discovery, ok := DiscoveryPluginStory[servicepath]; ok {
			return discovery
		} else {
			if dis, err := rpcxredisdis.NewRedisDiscovery(
				basepath,
				servicepath,
				[]string{parse.Address}, options); err != nil {
				log.Fatal("rpcx NewRedisDiscovery ERROR:", err)
			} else {
				DiscoveryPluginStory[servicepath] = &rpcx.DiscoveryPlugin{
					ServiceDiscovery: dis,
					AttrPlugin: rpcx.AttrPlugin{
						BASEPATH: basepath,
					},
				}
			}
		}
		return DiscoveryPluginStory[servicepath]
	}
}

// Peer2PeerRegisterPlugin 创建点对点服务注册组件（不可注册服务 为了同格式AttrPlugin）
func (sp *ShareingPlugin) Peer2PeerRegisterPlugin(basepath string) {
	sp.registerPlugin = shareing.Once[*rpcx.RegisterPlugin]{
		New: func() *rpcx.RegisterPlugin {
			return &rpcx.RegisterPlugin{
				RegisterPlugin: new(rpcx.P2PRegisterPlugin),
				AttrPlugin: rpcx.AttrPlugin{
					BASEPATH: basepath,
				},
			}
		},
	}
}

// MultipleServersDiscovery 创建点对点服务发现组件
func (sp *ShareingPlugin) MultipleServersDiscovery(serviceaddress map[string][]*client.KVPair, basepath string) {
	var DiscoveryPluginStory = map[string]*rpcx.DiscoveryPlugin{}
	sp.discoveryPlugin = func(servicepath string) *rpcx.DiscoveryPlugin {
		if discovery, ok := DiscoveryPluginStory[servicepath]; ok {
			return discovery
		} else {
			pairs := serviceaddress[servicepath]
			if dis, err := client.NewMultipleServersDiscovery(pairs); err != nil {
				log.Fatal("rpcx NewPeer2PeerDiscovery ERROR:", err)
			} else {
				DiscoveryPluginStory[servicepath] = &rpcx.DiscoveryPlugin{
					ServiceDiscovery: dis,
					AttrPlugin: rpcx.AttrPlugin{
						BASEPATH: basepath,
					},
				}
			}
		}
		return DiscoveryPluginStory[servicepath]
	}
}
